<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit|ie-comp|ie-stand">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta http-equiv="Cache-Control" content="no-siteapp" />
    <title>开发规范</title>
    <script src="../../static/kaige/js/szload.js"></script>
</head>

<body>
    <div class="kaige-body">
        <fieldset class="layui-elem-field layui-field-title">
            <legend>开发规范</legend>
        </fieldset>
        <ul class="layui-timeline">
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">写本文目的在于确立一系列切实可行性的准则，应用于本软件项目开发工作中，以提高开发效率及代码质量，使得项目代码更易于维护、拓展。
                    </h3>
                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">命名规范</h3>
                    <pre>
【清晰】，在合理理的范围内，名称应尽可能具有描述性。 
【一致】，相同含义的属性、变量、方法命名应尽可能保持一致，即使在不同的类中。
【适用】，但不限于变量、常量、属性、参数、方法、函数等。
【延续】，遵循ThinkPHP6的规范、Layui规范。
【严格】命名必须准确清晰，有明确的含义，且具有一定的描述性；不能以PHP、JS保留留字或关键字名命；
        命名应该具有上下文甚至全局一致性，如具备相同含义或者作用，命名方式应该相同或者类似。
</pre>
                </div>
            </li>

            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">不成文规范</h3>
                    <pre>
本项目不成文约定

数据表规定如下：
1、尽量仅使用 int 与 string 类型；
2、string 类型仅使用 [VARCHAR、TEXT、LONGTEXT]。
3、int 类型仅使用 [TINYINT、INT、BIGINT]，如果 BIGINT 不够用的话，请使用varchar由程序转换。
4、有小数位的数值型，用 decimal 类型，默认值0.00，须注意：不同mysql版本没有保留默认值0.00。
5、日期时间字段请使用 INT 11位 无符号 或 BIGINT 12位/13位 统一无符号。
6、主键一般情况用ID INT型 11位 无符号 自增，特殊情况请使用 varchar 64位，程序中使用 $id = autoid()。
7、某些字段需要定义索引，以便搜索时更快，这个看开发人员对MYSQL语句优化能力进行定义。

字段命名规则：
1、全部小写命名，禁止大写
2、采用26个英文字母(小写)、0-9的自然数、下划线"_"组成，命名简洁明确，多个单词或拼音用下划线分隔
3、简单的分组采用 主表_group方式命名(可用数据字典代替)
4、类型使用 type 非 class；最好是 表名_type，当然表名太长可使用 表名缩写_type
5、表名缩写一般采用 英文单词或拼单单词第一字母组合
6、表名前缀为 sz_模块简写_表名，明细表采用 sz_模块简写_主表名_item方式命名(例：sz_hrm_dept部门表，sz_sys_dict系统字典表 sz_hrm_xxxx_item某表的明细表)
7、创建时间 create_time/ctime,更新时间update_time/utime,删除标识(时间)统一使用：delete_time（统一删除方法中，会查找是否存在delete_time为软删除），默认值为0/NULL；
8、操作人 handle_name，操作时间handle_time,操作人ID:handle_uid
9、申请人 put_name,申请时间 put_time,申请人ID：put_uid
10、关联字段：例 部门 dept_id,部门名称 dept_id_name; 以表名(去前缀)_id,表名_id_name；当关联复数时例：dept_ids,dept_ids_name值以","隔开且两个字段数组下标需要对应
11、log代表日志，记录。例：user_login_log 登录日志，这条不强制，但建议。
12、拼音与英文允许混着使用，限一些常见的英文，下划线连接，如果非常见请使用全拼音或全英文。
13、关联用户表的ID建议使用 uid/user_id
14、编号用 bn(也可 表名_bn)，编码 code;

补充：尽可能使用数据字典(系统字典)选项。数据字典会默认加载（id,name,value,explain）explain是说明的意思。


PHP规定

1、参照 ThinkPHP 6.0 的开发规范
2、控制器方法命名几点统一： add=添加 edit=编辑 del=删除 multi_del 批量删除(因权限问题，需要分开单个/批量) list=列表，getinfo=单条记录信息(只提供json数据)，look=查看; 
3、非特殊方法不能使用下划线开头。如果控制器方法不暴露在节点中，请使用下划线开头或非public。
4、model层 只是简单定义、表的关联（TP6的方法）；
5、dao层是数据访问层(数据操作层)，增删改查数据全在dao层；复杂的关联使用sql语句，尽量写得清晰直观。
6、Service层在此系统中作为业务层；
7、简单说一下执行层级：html->controller->Service->dao->model->dao->Service->controller->html;数据验证可放在控制器层里，放业务层也可，似情况而定。控制器负责接收、验证数据、提供数据给html;
8、开发前认填读 Traits里的封装方法，不要在重构轮子；对轮子发现bug需及时在项目组里反应或修正再反应，必须说明原由、修正结果；

补充：
插一条说明：因需要多端统一，故本系统禁止使用session，使用JWT方式验证客户端用户识别，系统使用redis缓存，必须配置redis；
在控制器层 可以使用 $this->user(用户对象)获取用户信息，如：用户账户名 $this->user->un,ID $this->user->uid，用户信息也必须在控制器层传入server层，server不做request接收，只以参数方式接收;非简单的参数，请以数组形式传参，方便扩展，潜规则：超过5个参数，以数组传参，主键、用户ID例外 如 调用xxxx($id,$uid,$array)，方法xxxx($id = 0,$uid = 0,$array = [])。


本系统是基于ThinkPHP6 + Layui开发的，前端框架用到Layui；
【Layui扩展】目录指定：/static/kaige/layuiextend/ 所有扩展均放在此目录。
【关于封装JS】封装的JS 必须是 可复用的，项目里2次以上调用的。写明如何调用或直接示例。
【注释】 类、方法、数据表、字段均需要写说明
【节点获取与菜单配置】节点获取后，先填好节点的名称，后一定要刷新页面，填上菜单ID,父ID，选择是否菜单，配置好 权限选项！！！
</pre>

                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">类、方法注释示例</h3>
                    <pre class="layui-code" lay-title="PHP">
/**
* 某控制器
* @author:作者昵称
* @date: 时间
* @copyright ©2022 泉州市盛筑信息科技有限公司 版权所有
*/
Class Xxxx
{
    /**
    * 用途
    * @param $str 说明   ----如果有修改加上谁修改，修改哪行最好也标注一下，一个方法如果修改超过10行，请重新定义一个。
    * @author:作者昵称 --可填可不填，如与类的作者不一致，还是填一下
    * @edit:作者 + 时间 --修改时必填
    */
    function xxx($str){

    }
}

操作数据公共方法如下：

/**
* 根据主键获取信息 - 接口
* @param int $id 主键
* @param array|bool $where 查询条件
* @param array $field 需要显示的字段
* @return mixed 返回实例对象 
*/
getInfoById($id, $where = true, $field = [])

/**
* 根据查询条件获取信息 - 接口
* @param array|bool $where 查询条件
* @param array $field 需要显示的字段
* @return mixed 返回实例对象
*/
getInfoByWhere($where = true, $field = [])

/**
* 根据默认查询字段查询 - 接口
* @param mixed $name 默认字段
* @param array|bool $where 查询条件
* @return mixed 返回实例对象
*/
getInfoByName($name, $where = true, $field = [])

/**
* 查询满足条件的数量 - 接口
* @param array|bool $where 查询条件
* @return int 返回数量，无数据返回0
*/
getTotal($where = true)

/**
* 分页查询 - 接口
* @param int $page 当前页
* @param int $limit 每页数量 默认 20
* @param array|bool $where 查询条件
* @param array $order 排序规则
* @param array $field 显示的字段
* @return array 返回数组
*/
getLists($page = 1, $limit = 20, $where = true, $order = ['id' => 'asc'], $field = [])

/**
* 查询所有数据 - 接口
* @param array|bool $where 查询条件
* @param array $order 排序
* @return array
*/
getListAll($where = true, $order = ['id' => 'asc'], $field = [])

/**
* 新增 - 接口
* @param array $data
* @return mixed 返回实例对象或false
*/
add($data)

/**
* 更新数据 - 接口
* @param int $id
* @param array $data
* @return mixed
*/
update($id, $data)

/**
* 根据主键删除单条数据 - 接口
* @param int $id
* @return bool
*/
delete($id)

/**
* 批量删除 $ids不能=0 - 接口
* @param array|string $ids  string:'1,2,...'
* @return bool
*/
multiDelete($ids)

/**
* 单字段值更新-允许INT字段，必须在model设置允许的字段 uaField=[...] - 接口
* @param int $id
* @param string $field
* @param int $val
* @return bool
*/
updateField($id = 0, $key = '', $val = 0)

/**
* 根据主键获取单字段值 - 接口
* @param int $id 主键
* @param string $field 字段名
* @return mixed
*/
oneField($id = 0, $field = '')


//--------------------------关联取列表（开始）--------------------------
/**
* 列表是否关联 格式 'a,b,c' / ['a','b','c']
* 如果存在关联，在model层要加入关联的(with) 函数
*/
public function _list_with()
{
    return [];
}
/**
* 列表查询条件
*/
public function _list_where($data)
{
    return [
    'where' => $data['where'],
    'order' => $data['order'],
    'field' => $data['field']
    ];
}

/**
* 格式化数据
*/
public function _list_format($data)
{
    return $data;
}

/**
* 控制关联子表的字段部分显示
*/
public function _list_visible()
{
    //在service层定义此方法，返回数组。['关联with函数名' => ['字段1', '字段2'], '关联with函数名' => ['字段1', '字段2'], ...];
    return [];
}
//--------------------------关联取列表（结束）--------------------------

/**
* 获取关联列表数据，默认分页
* @param array $data
* @param boolean $isPage
* @return array [count,data]
*/
_list($data, $isPage = true)

/**
* 查询已经软删除的列表，无关联
* @param array $data 查询条件
* @param bool $isPage 是否分页 默认分页
* @return mixed
*/
getRecycleList($data = [], $isPage = true)

/**
* 保存表单数据
* @param $data
* @return array
*/
saveForm($data)

/**
* 删除数据，支持批量删除 - 接口
* @param string $id 
* @return array
*/
_del($id)

/**
* 根据主键获取一条记录 - 兼容旧版
* 建议使用 getInfoById
* @return array
*/
_one($id = 0)

</pre>
                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">后端目录</h3>
                    <pre class="layui-code" lay-title="后端目录">
    www WEB部署目录
    ├─app 应用目录
    │ ├─api
    │ │  ├─controller 控制器目录
    │ │  ├─ ... 更多api模块的目录
    │ ├─backend
    │ │  ├─controller 控制器目录
    │ │  │  ├─sys 分为系统模块
    │ │  │  ├─... 其它模块
    │ │  ├─ ... 更多backend模块的目录
    │ ├─common
    │ │  ├─controller 公用控制器目录
    │ │  ├─dao DAO层目录
    │ │  │  ├─sys 分为系统模块
    │ │  │  ├─... 其它模块
    │ │  ├─exception  自定义异常
    │ │  ├─model MODEL层目录
    │ │  │  ├─sys 分为系统模块
    │ │  │  ├─... 其它模块
    │ │  ├─service SERVICE层目录
    │ │  │  ├─sys 分为系统模块
    │ │  │  ├─... 其它模块
    │ │  ├─traits 共有特性目录
    │ │  ├─ ... 更多
    │ ├─index
    │ │  ├─controller 控制器目录
    │ ├─model 模型目录
    │ ├─ ... 更多类库目录
    │ │
    │ ├─common.php 公共函数文件
    │ └─event.php 事件定义文件
    │
    ├─config 配置目录
    │ ├─app.php 应用配置
    │ ├─cache.php 缓存配置
    │ ├─console.php 控制台配置
    │ ├─cookie.php Cookie配置
    │ ├─database.php 数据库配置
    │ ├─filesystem.php 文件磁盘配置
    │ ├─lang.php 多语言配置
    │ ├─log.php 日志配置
    │ ├─middleware.php 中间件配置
    │ ├─route.php URL和路由配置
    │ ├─session.php Session配置
    │ ├─trace.php Trace配置
    │ ├─error.php 提示信息统一代码
    │ └─kaige.php 站点配置
    │
    ├─route 路由定义目录
    │ ├─route.php 路由定义文件
    │ └─ ...
    │
    ├─public WEB目录（对外访问目录）
    │ ├─index.php 入口文件
    │ ├─router.php 快速测试文件
    │ └─.htaccess 用于apache的重写
    │
    ├─extend 扩展类库目录
</pre>
                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">MODEL层 模板</h3>
                    <pre class="layui-code" lay-title="MODEL层 模板">

    declare(strict_types=1);
    
    namespace app\common\model\sys;
    
    use think\Model;
    use think\model\concern\SoftDelete;    // 含有软删除的才需要
    use app\common\traits\ModelTrait;
    
    /**
     * 应用客户端-模型  
     * @author:kaige
     * @date:2022-06-12
     * @copyright ©2022 泉州市盛筑信息科技有限公司 版权所有
     */
    class SysClient extends Model
    {
        use SoftDelete; // 含有软删除的才需要
        //软删除时间
        protected $deleteTime = 'delete_time';  // 含有软删除的才需要
        //软删除字段的默认值
        protected $defaultSoftDelete = 0;  // 含有软删除  值不为null的 才需要
        //开启自动写入时间戳字段
        protected $autoWriteTimestamp = true;
        protected $createTime = 'create_time'; 
        protected $updateTime = 'update_time';
    
        // 设置字段信息(避免多一次查询的开销)
        protected $schema = [          // 必须填写 对应 数据表里的字段
            'id'            => 'int',
            'app_id'        => 'string',
            'app_salt'      => 'string',
            'name'          => 'string',
            'reg_ip'        => 'string',
            'last_time'     => 'int',
            'last_ip'       => 'string',
            'desc'          => 'string',
            'status'        => 'int',
            'create_time'   => 'int',
            'update_time'   => 'int',
            'delete_time'   => 'int',
        ];
    
        use ModelTrait;
    }
    
</pre>
                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">DAO层(数据访问层)模板</h3>
                    <pre class="layui-code" lay-title="DAO层(数据访问层)模板">


    declare(strict_types=1);
    
    namespace app\common\dao\sys;
    
    use app\common\model\sys\SysClient; // 此处修改对应的model
    use app\common\traits\DaoTrait;
    
    /**
     * 应用客户端        //此处修改 什么 DAO
     * @author:kaige     //开发者
     * @date:2022-06-12  //日期
     * @copyright ©2022 泉州市盛筑信息科技有限公司 版权所有
     */
    class ClientDao  // 此处修改对应的名称
    {
      // 当前操作的模型
      protected $model = null;
    
      // 对应模型的主键，默认为id
      protected $pk = 'id';
    
      // 对应模型默认查询的字段
      protected $name = 'app_id';  //此处修改 除了主键的另一个重要字段名
    
      // 对应模型允许查询的字段(默认全部查询)
      protected $allowField = ['*'];
    
      // 允许单字段更新
      protected $uaField = ['status'];
    
      // 查询结果-要隐藏的字段 默认空数组
      protected $hideField = [];
    
      public function __construct()
      {
        $this->model = new SysClient(); // 此处修改对应的model
      }
      use DaoTrait;
    }
    
</pre>
                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">DAO层(数据访问层)模板</h3>
                    <pre class="layui-code" lay-title="Service层(业务层)模板">

    declare(strict_types=1);
    
    namespace app\common\service\sys;
    
    use app\common\traits\ServiceTrait;
    use app\common\dao\sys\ClientDao;
    
    /**
     * 应用客户端
     * @author:kaige
     * @date:2022-06-12
     * @copyright ©2022 泉州市盛筑信息科技有限公司 版权所有
     */
    class ClientService
    {
      protected $dao = null;
      
      public function __construct()
      {
        $this->dao = new ClientDao();
      }
      use ServiceTrait;
    
     
    }
    
</pre>
                </div>
            </li>
            <li class="layui-timeline-item">
                <i class="layui-icon layui-timeline-axis layui-icon-note"></i>
                <div class="layui-timeline-content layui-text">
                    <h3 class="layui-timeline-title">controller(控制器)模板</h3>
                    <pre class="layui-code" lay-title="controller(控制器)模板">
    namespace app\backend\controller;
    
    use app\common\controller\Basic;
    use app\common\service\sys\FilesGroupService;
    use app\common\service\sys\FilesService;
    use app\common\service\hrm\CompanyService;
    
    /**
     * 上传文件控制器
     * @auth kaige
     * @date 2022-05-07
     */
    class Files extends Basic
    {
      protected $fgservice;
      // 初始化
      protected function initialize()
      {
        parent::initialize();
        // 当前控制器服务实例化
        $this->service = new FilesService();
        $this->fgservice = new FilesGroupService();
      }
      /**
       * 列表
       */
      public function index()
      {
        if ($this->request->isGet()) {
          $data = input('param.');
          return json($this->service->_list($data));
        }
        return _jerr(1000);
      }
    
      /**
       * 删除
       */
      public function del()
      {
        if ($this->request->isPost()) {
          $id = input('id/s', '');
          return json($this->service->delFiles($id));
        }
        return _jerr(1000);
      }

    }

</pre>

                </div>
            </li>
        </ul>
    </div>
    <script>
        load_init_js(function () {
            layui.use(['element', 'code'], function () {
                var element = layui.element;
                var $ = layui.$;
                layui.code({
                    skin: 'vscode',
                    lang: ''
                });
            });
        });
    </script>
</body>

</html>